Data Memory is EEPROM which holds values even after power is removed.
It requires a complex procedure to read or write and needs 10 milliseconds or so
to 'burn' the information into memory. Data addresses run from 0-63 decimal.
The PIC communicates with the outside world through it's pins.
The pins are divided into 2 ports, Port A and Port B. Port B controls 8 pins,
RB0-RB7,(see top view above). Port A controls 5 pins, RA0-RA4.
These ports are controlled through port 'registers'. Microchip
likes to call these 'file' registers and assigns them the letter 'f', (in
general). Learn to manipulate the registers and you learn to program the PIC.
Each port has 2 registers associated with it. PORTA and PORTB are the names of
the registers that show the state of the pins, high or low. TRISA and TRISB are
registers that indicate which pins are inputs and which are outputs.
There is also a 'working' register 'W'. Almost everything has
to go through the 'W' register to end up anywhere else. It usually take two
instructions to do something. First load 'W' with something, (1), and then, (2),
put the contents of 'W' into a register.
This process is used in the above program to set up the ports.
In the TRIS registers a 0 represents an output and a 1 an input. We want to set
all pins to outputs. Pins set as inputs should always be pulled high or low. An
input should never be left floating. Setting all pins as outputs takes care of
those pins not connected to anything, (no inputs).
'movlw' stands for: move a literal, (number), into the 'W'
register. We move the binary number B'00000000' into W in the first instruction.
The binary representation lets you see all the bits.
The 'tris' instruction takes whatever is in W and puts it into
the TRIS register associated with the PORT register you specify. 'tris PORTA'
puts the 0 into TRISA and 'tris PORTB' puts the same number into TRISB.
There is another way to set up the TRIS registers that involves
'page flipping', but ignore it for now. The tris instruction is the easiest way
to set up TRIS registers.
Once the TRIS register bits are all set to outputs, the numbers
we put in the PORT registers determines what levels are on the pins. 1's will
make the pins high and 0's will make them low. We have LEDS on the 4 lowest bits
of Port B, (RB0-RB3), so the lowest 4 bits in PORTB will determine if these are
on, (1's), or off, (0's).
The 'clrf PORTB' instruction turns all the LED's off. It
'clears' the register named, (sets all bits to zero). In words: 'clear the
indicated register'.
The 'incf PORTB, f' instruction finds out what is in register
PORTB, adds one to it and puts that number in PORTB. This makes the LEDs 'count'
in binary. In words: 'increment the indicated register'.
Did you notice the 'f' at the end of 'incf PORTB, f'? Many
instructions can do double duty because the result can be placed either in the
register indicated in the instruction, ('f'), or in the 'W' register. 'W' or 'f'
is referred to as the 'destination' of the instruction. It is important to note
that if the result is placed in 'W', the original register is not changed.
Another register TMR0, (timer zero), is involved in setting the
delay between counts. TMR0 continually increments at a certain rate. When it
gets to B'11111111' the next count 'rolls over' to B'00000000'. At this time a
certain bit in the register INTCON, (T0IF), is 'set', (made 1). We can watch for
this to happen and know that 256 counts have passed since TMR0 was at zero. The
counts happen at a rate of the crystal frequency divided by four. For the watch
crystal this means every 0.122 milliseconds.
The register INTCON has eight bits dealing with interrupts. The
only one we are concerned with now is bit 2 which is the TMR0 overflow interrupt
flag bit. If this bit is one, TMR0 has overflowed, if it is zero, it has not.
You are responsible for resetting this flag yourself in your program once it has
overflowed.
Testing a bit is done using an instruction that skips over then
next instruction if the bit has a certain value. 'BTFSS' stands for: 'test the
bit designated and skip the next instruction if the bit is 1, (set). If we make
the next instruction a 'goto' then the program either goes to this new location
if the bit is clear, (0) or continues on if it is 1. There is also the
instruction 'BTFSC' which looks for a clear bit rather than a set bit. The
designated bit can be a number, (0-7), or a symbol which is equated with a
number such as 'T0IF' in this case. The number is assigned to T0IF in the file
"p16F84.inc" which was included at the top of the program.
Normally instructions are carried out in sequence. To break up
this sequence and go to another location we use the 'goto' instruction. This
instruction usually contains a label which tells where to go next. A variation
on this is the use of '$' which stands for current instruction. Adding a -1
means go to the previous instruction, a +2 would be the one after the next and
so on. The '$' convention is useful for short jumps. Longer jumps should use
labels.
We can effectively slow down the timer by factors of 2, 4, 8,
16 .... 256 by using what is called a prescalar. This involves setting the bits
of yet another register, OPTION. The OPTION register can be set by using the
option command. The lowest 3 bits of OPTION designate the eight different ratios
mentioned. It turns out that if we set the prescalar to 1:32, the rollover
happens using a watch crytal at exactly one second. The lowest three bits should
be binary '100' to give 1:32.
On power up, all bits of OPTION are set to 1. A '1' in bit 3,
PSA, (prescalar assignment bit), assigns the prescalar to the watchdog timer
rather than TMR0. We definately want that one to be '0'. A '1' in bit 5, T0CS,
(timer zero clock source select bit), makes the register TMR0 respond to
transitions on pin 3 of the PIC, (RA4). We want that one to be zero also. In the
program listing, you can see where W is set to B'00000100' and put into OPTION.
'bcf' stands for: 'clear a certain bit in the designated
register. If we are going to see the next rollover, we have to somehow clear the
flag so we can see when it is set again. There is also a 'bsf' to set bits.
The program is a continuous loop. You travel down through
instructions until you reach the last which is 'goto loop' which sends you back
to the instruction just after the label 'loop:'. Labels have to start in column
one. Just about everything else can not start in column one.
It isn't hard to change the speed of the counter in multiples
of 2. Just change the lowest three bits of OPTION. '000' gives a ratio of 1:2,
'001' a ratio of 1:4 and so on. Vary these bits and see what happens to the
counter.
Does the pattern of incrementing binary numbers make sense to
you? If you need a refresher on numbering systems you could try: http://www.chesworth.com/pv/technical/computing.numbers.htm.
Watch
the counter until you are familiar with the pattern. Can you guess what the
pattern of the high 4 bits of PORTB is like? A single resistor and LED can be
made into a probe that you can use to examine these port pins. A low current LED
is nice for this because you don't have to worry about drawing too much from the
pins.
A decrementing counter
How would you make the numbers decrement rather than increment? There is an
instruction 'decf' which could replace the 'incf'. What happens after you reach
zero? Could you add instructions to make the counting start at a particular
number, say 10. You can represent a decimal ten as D'10'. The same number in
hexadecimal is H'A'. In binary it is B'00001010'. Remember you usually have to
go through 'W'.
Count every other number
How would you count only even numbers, (the rightmost LED would never come
on)? See if you can write and run the program. How about only odd numbers?
Stop the count at a certain number
You could add four more LED's and count all the way to 255, but four should
give you the idea of what is happening. Instead, why not stop when you reach 16,
(H'10'), and start decrementing. You have seen the instruction 'btfss
(register), (bit)'. See if you can use this to detect when bit 4, ( 5th from the
right ), gets set and then start counting down.
But now, what happens at zero? You then want to start incrementing again. We
need an 'btfss' that detects not when a certain bit is set but when no bits are
set. There is a register that has such a bit. It is the 'Z ' bit of the STATUS
register which is set when an instruction gives a result of zero. So use 'btfss
STATUS, Z' and write a program that continually counts up and down between 0 and
15, (or 1 and 16 or whatever).
The 'Nightrider' display
What if you want to 'roll' one lit LED across the display, by doing the
number sequence 1,2,4,8,1,2,4,8 etc. Could you write a program to do it? There
is an instruction that rolls all bits to the left one place. It is called 'rlf
(register), f'. Use it to do the roll program. Then try adding btfss
instructions to create the sequence 1,2,4,8,4,2,1,2,4 etc.